/* eslint-disable */
export module XMLParser {
  const specialSymbolArray: [RegExp, string][] = [
    [/&lt;/g, '<'],
    [/&gt;/g, '>'],
    [/&amp;/g, '&'],
    [/&quot;/g, '"'],
    [/&apos;/g, "'"],
    [/&nbsp;/g, ' '],
  ];

  function escapeSpecialSymbol(text: string) {
    for (let i = 0; i < specialSymbolArray.length; ++i) {
      const [reg, replace] = specialSymbolArray[i];
      text = text.replace(reg, replace);
    }
    return text;
  }

  export interface IXMLJson {
    tag: string;
    text: string;
    children: IXMLJson[];
    attrs: { [key: string]: string };
  }

  export interface IXMLNode {
    // tag
    tag: string;
    // 文本
    text: string;
    // 自身的属性
    attrs: { [key: string]: string };
  }

  export class IXMLTree {
    // 继承的属性
    attrs: { [key: string]: string } = {};
    // 节点
    node: IXMLNode = null;
    // 子树
    children: IXMLTree[] = [];
    // 父树
    parent: IXMLTree = null;

    toJson(): IXMLJson {
      return {
        tag: this.node.tag,
        text: this.node.text,
        children: this.children.map((child) => child.toJson()),
        attrs: this.attrs,
      };
    }
  }

  function createTree(node: IXMLNode, parent?: IXMLTree) {
    const tree = new IXMLTree();
    tree.node = node;
    if (parent) {
      Object.assign(tree.attrs, parent.attrs, node.attrs);
      tree.parent = parent;
      parent.children.push(tree);
    } else {
      Object.assign(tree.attrs, node.attrs);
    }
    return tree;
  }

  function createTextNode(text: string): IXMLNode {
    return {
      tag: '#text',
      text: escapeSpecialSymbol(text),
      attrs: {},
    };
  }

  /**
   * 创建节点
   * @param tag tag attr1=value1 attr2=value2
   */
  function createNode(tag: string): IXMLNode {
    const split = tag.split(' ');
    const result: IXMLNode = {
      tag: split[0],
      text: '',
      attrs: {},
    };
    // 解析属性
    for (let index = 1; index < split.length; index++) {
      const attrText = split[index].split('=');
      if (!attrText[0]) continue;
      if (attrText.length === 2) {
        result.attrs[attrText[0]] = attrText[1];
      } else {
        result.attrs[attrText[0]] = '';
      }
    }
    return result;
  }

  /**
   * 解析xml
   * @param xmlText xml文本
   */
  export function parseXML(xmlText: string): IXMLTree {
    let startIndex = 0;
    const length = xmlText.length;

    const headTree = createTree(createNode('#root'));
    let currTree = headTree;

    let count = 0;
    while (startIndex < length) {
      if (count++ > length) {
        console.error('xml解析次数超过上限');
        break;
      }
      const tagEndIndex = xmlText.indexOf('>', startIndex);
      // 未找到结束符
      if (tagEndIndex === -1) {
        const node = createTextNode(xmlText.substring(startIndex));
        createTree(node, currTree);
        break;
      }
      const tagStartIndex = xmlText.lastIndexOf('<', tagEndIndex);
      // 未找到开始符
      if (tagStartIndex === -1) {
        const node = createTextNode(xmlText.substring(startIndex));
        createTree(node, currTree);
        break;
      }
      // 文本开始到开始符之间有内容
      if (tagStartIndex > startIndex) {
        const node = createTextNode(xmlText.substring(startIndex, tagStartIndex));
        createTree(node, currTree);
      }
      if (xmlText.charAt(tagStartIndex + 1) === '?' && xmlText.charAt(tagEndIndex - 1) === '?') {
        // XML声明标签
      } else if (xmlText.charAt(tagStartIndex + 1) === '/') {
        // 结束标签
        const tag = xmlText.substring(tagStartIndex + 2, tagEndIndex);
        const tagName = tag.split(' ')[0];
        if (currTree.node.tag === tagName) {
          if (currTree.parent) {
            currTree = currTree.parent;
          }
        } else {
          // 错误的结束标签，忽略
          // const node = createTextNode(tag);
          // createTree(node, currTree);
        }
      } else if (xmlText.charAt(tagEndIndex - 1) === '/') {
        // 自闭标签
        const tag = xmlText.substring(tagStartIndex + 1, tagEndIndex - 1);
        const node = createNode(tag);
        createTree(node, currTree);
      } else {
        // 开始标签
        const tag = xmlText.substring(tagStartIndex + 1, tagEndIndex);
        const node = createNode(tag);
        currTree = createTree(node, currTree);
      }
      startIndex = tagEndIndex + 1;
    }
    return headTree;
  }
}
